// (C) 2021 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

module decoder_8b10b
    #(
        parameter METHOD = 1,
        parameter RDERR = 1,
        parameter KERR = 1
    )
    (
    input wire       clk,
    input wire       rst,            
    input wire       din_ena,            // 10b data ready
    input wire [9:0] din_dat,            // 10b data input
    input wire       din_rd,             // running disparity input
    output reg       dout_val,           // data out valid        
    output reg [7:0] dout_dat,           // data out
    output reg       dout_k,             // special code
    output reg       dout_kerr,          // coding mistake detected
    output reg       dout_rderr,         // running disparity mistake detected
    output wire      dout_rdcomb,        // running disparity output (comb)
    output reg       dout_rdreg          // running disparity output (reg)
);

wire a = din_dat[0];
wire b = din_dat[1];
wire c = din_dat[2];
wire d = din_dat[3];
wire e = din_dat[4];
wire i = din_dat[5];
wire f = din_dat[6];
wire g = din_dat[7];
wire h = din_dat[8];
wire j = din_dat[9];


//classification
wire P04 = (!a & !b & !c & !d);
wire P13 = (!a & !b & !c & d) | (!a & !b & c & !d) | (!a & b & !c & !d) | (a & !b & !c & !d);
wire P22 = (!a & !b & c & d) | (!a & b & c & !d) | (a & b & !c & !d) | (a & !b & c & !d) | (a & !b & !c & d)  | (!a & b & !c & d);
wire P31 = (a & b & c & !d) | (a & b & !c & d) | (a & !b & c & d) | (!a & b & c & d);
wire P40 = (a & b & c & d);


////////////////////////////////////////////////
// data outputs
////////////////////////////////////////////////

wire A = (P22 & !b & !c & !(e^i)) ? !a :
         (P31 & i) ? !a :
         (P13 & d & e & i) ? !a :
         (P22 & !a & !c & !(e^i)) ? !a :
         (P13 & !e) ? !a :
         (a & b & e & i) ? !a :
         (!c & !d & !e & !i) ? !a :
         a;
wire B = (P22 & b & c & !(e^i)) ? !b :
         (P31 & i) ? !b :
         (P13 & d & e & i) ? !b :
         (P22 & a & c & !(e^i)) ? !b :
         (P13 & !e) ? !b :
         (a & b & e & i) ? !b :
         (!c & !d & !e & !i) ? !b :
         b;
wire C = (P22 & b & c & !(e^i)) ? !c :
         (P31 & i) ? !c :
         (P13 & d & e & i) ? !c :
         (P22 & !a & !c & !(e^i)) ? !c :
         (P13 & !e) ? !c :
         (!a & !b & !e & !i) ? !c :
         (!c & !d & !e & !i) ? !c :
         c;
wire D = (P22 & !b & !c & !(e^i)) ? !d :
         (P31 & i) ? !d :
         (P13 & d & e & i) ? !d :
         (P22 & a & c & !(e^i)) ? !d :
         (P13 & !e) ? !d :
         (a & b & e & i) ? !d :
         (!c & !d & !e & !i) ? !d :
         d;
wire E = (P22 & !b & !c & !(e^i)) ? !e :
         (P13 & !i) ? !e :
         (P13 & d & e & i) ? !e :
         (P22 & !a & !c & !(e^i)) ? !e :
         (P13 & !e) ? !e :
         (!a & !b & !e & !i) ? !e :
         (!c & !d & !e & !i) ? !e :
         e;


wire F = (f & h & j) ? !f :
         (!c & !d & !e & !i & (h^j)) ? !f :
         (!f & !g & h & j) ? !f :
         (f & g & j) ? !f :
         (!f & !g & !h) ? !f :
         (g & h & j) ? !f :
         f;
wire G = (!f & !h & !j) ? !g :
         (!c & !d & !e & !i & (h^j)) ? !g :
         (!f & !g & h & j) ? !g :
         (f & g & j) ? !g :
         (!f & !g & !h) ? !g :
         (!g & !h & !j) ? !g :
         g;
wire H = (f & h & j) ? !h :
         (!c & !d & !e & !i & (h^j)) ? !h :
         (!f & !g & h & j) ? !h :
         (f & g & j) ? !h :
         (!f & !g & !h) ? !h :
         (!g & !h & !j) ? !h :
         h;


wire K = (c & d & e & i) |
         (!c & !d & !e & !i) |
         (P13 & !e & i & g & h & j) |
         (P31 & e & !i & !g & !h & !j);


////////////////////////////////////////////////
//running disparity - generate and err check
////////////////////////////////////////////////
wire rd1n = (P04) ? 1 :
            (P13 & !(e & i)) ? 1 :
            (P22 & !e & !i) ? 1 :
            (P13 & d & e & i) ? 1 :
            0 /* synthesis keep */;
wire rd1p = (P40) ? 1 :
            (P31 & !(!e & !i)) ? 1 :
            (P22 & e & i) ? 1 :
            (P31 & !d & !e & !i) ? 1 :
            0 /* synthesis keep */;
wire rd1e = (P13 & !d & e & i) ? 1 :
            (P22 & (e ^ i)) ? 1 :
            (P31 & d & !e & !i) ? 1 :
            0 /* synthesis keep */;

wire rd1_err = (!din_rd & rd1n) | (din_rd & rd1p);

/////////////////////////////
// factored rd1 generation
/////////////////////////////
wire [63:0] rd1_when_din_rd_0_mask = 64'hffe8e880e8808000;
wire rd1_when_din_rd_0 = rd1_when_din_rd_0_mask[din_dat[5:0]] /* synthesis keep */;
wire [63:0] rd1_when_din_rd_1_mask = 64'hfffefee8fee8e800;
wire rd1_when_din_rd_1 = rd1_when_din_rd_1_mask[din_dat[5:0]] /* synthesis keep */;
wire rd1 = din_rd ? rd1_when_din_rd_1 : rd1_when_din_rd_0;

wire rd2n = (!f & !g & !h) ? 1 :
            (!f & !g & !j) ? 1 :
            (!f & !h & !j) ? 1 :
            (!g & !h & !j) ? 1 :
            (!f & !g & h & j) ? 1 :
            0 /* synthesis keep */;
wire rd2p = (f & g & h) ? 1 :
            (f & g & j) ? 1 :
            (f & h & j) ? 1 :
            (g & h & j) ? 1 :
            (f & g & !h & !j) ? 1 :
            0 /* synthesis keep */;
wire rd2e = ((f ^ g) & (h ^ j)) ? 1 :
            0;

wire rd2_err = (!rd1 & rd2n) | (rd1 & rd2p);

wire dout_rdcomb_special = (!f & !g &  h &  j) |
                            ( f &  g & !h & !j) /* synthesis keep */;

assign dout_rdcomb = (rd2p) ? !dout_rdcomb_special :
                   (rd2n) ? dout_rdcomb_special :
                   rd1;

wire k_err;
generate
    if (METHOD == 0) begin

        assign k_err = //5b6b errors
             (P04) ? 1 :
             (P13 & !e & !i) ? 1 :
             (P31 & e & i) ? 1 :
             (P40) ? 1 :
             //3b4b errors
             ( f &  g &  h &  j) ? 1 :
             (!f & !g & !h & !j) ? 1 :

             //any 2nd phase rd error, except if rd1 is even
             (rd2_err & !rd1e) ? 1 :

             ( a &  b & !c & !d & !e & !i &  f &  g &  h & !j) ? 1 :
             (                     e &  i &  f &  g &  h & !j) ? 1 :

             (!a & !b &  c &  d &  e &  i & !f & !g & !h &  j) ? 1 :
             (                    !e & !i & !f & !g & !h &  j) ? 1 :

             (!(P22 & !c & !d)        & !e & !i & !f & g & h & j) ? 1 :
             (!(P13)                  & !e &  i & !f & g & h & j) ? 1 :
             (!(P13 & (a | b | c))    &  e &  i & !f & g & h & j) ? 1 :
             (                           e & !i & !f & g & h & j) ? 1 :

             (!(P22 & c & d)          &  e &  i & f & !g & !h & !j) ? 1 :
             (!(P31)                  &  e & !i & f & !g & !h & !j) ? 1 :
             (!(P31 & (!a | !b | !c)) & !e & !i & f & !g & !h & !j) ? 1 :
             (                          !e &  i & f & !g & !h & !j) ? 1 :

             0;
    end
    else if (METHOD == 1) begin

        wire [63:0] kerr_mask_ai = 64'h6881800180018117;                               
        wire [63:0] kerr_mask_ej = 64'hf20000018000004f;
        wire kerr_out_ai =  kerr_mask_ai[din_dat[5:0]] /* synthesis keep */;
        wire kerr_out_ej =  kerr_mask_ej[din_dat[9:4]] /* synthesis keep */;
        wire rd2_err_lc = rd2_err /* synthesis keep */;

        wire kerr6,kerr7,kerr8,kerr9,kerr_remainder;
        stratixii_lcell_comb kerr6_I (
            .datab(!din_dat[7]),
            .datac(!din_dat[6]),
            .datad(!din_dat[8]),
            .datae(!din_dat[9]),
            .dataa(1'b1),.dataf(1'b1),.datag(1'b1),
            .cin(1'b1),.sharein(1'b0),.sumout(),.cout(),.shareout(),
            .combout(kerr6 ));
        defparam kerr6_I .shared_arith = "off";
        defparam kerr6_I .extended_lut = "off";
        defparam kerr6_I .lut_mask = 64'h0C0000300C000030;

        stratixii_lcell_comb kerr7_I (
            .dataa(!din_dat[3]),
            .datab(!din_dat[7]),
            .datac(!din_dat[6]),
            .datad(!din_dat[8]),
            .datae(!din_dat[9]),
            .dataf(1'b1),.datag(1'b1),
            .cin(1'b1),.sharein(1'b0),.sumout(),.cout(),.shareout(),
            .combout(kerr7 ));
        defparam kerr7_I .shared_arith = "off";
        defparam kerr7_I .extended_lut = "off";
        defparam kerr7_I .lut_mask = 64'h0002403000024030;

        stratixii_lcell_comb kerr8_I (
            .dataa(!din_dat[0]),
            .datab(!din_dat[1]),
            .datac(!din_dat[2]),
            .datad(!din_dat[4]),
            .datae(!din_dat[3]),
            .dataf(1'b1),.datag(1'b1),
            .cin(1'b1),.sharein(1'b0),.sumout(),.cout(),.shareout(),
            .combout(kerr8 ));
        defparam kerr8_I .shared_arith = "off";
        defparam kerr8_I .extended_lut = "off";
        defparam kerr8_I .lut_mask = 64'h6868960868689608;

        stratixii_lcell_comb kerr9_I (
            .dataa(!din_dat[0]),
            .datab(!din_dat[1]),
            .datac(!din_dat[2]),
            .datad(!din_dat[4]),
            .datae(!din_dat[3]),
            .dataf(1'b1),.datag(1'b1),
            .cin(1'b1),.sharein(1'b0),.sumout(),.cout(),.shareout(),
            .combout(kerr9 ));
        defparam kerr9_I .shared_arith = "off";
        defparam kerr9_I .extended_lut = "off";
        defparam kerr9_I .lut_mask = 64'h1001161E1001161E;

        stratixii_lcell_comb kerr_rem_I (
            .dataa(!din_dat[5]),
            .datab(!kerr6 ),
            .datac(!kerr7 ),
            .datad(!din_dat[4]),
            .datae(!kerr8 ),
            .dataf(!kerr9 ),
            .datag(1'b1),
            .cin(1'b1),.sharein(1'b0),.sumout(),.cout(),.shareout(),
            .combout(kerr_remainder ));
        defparam kerr_rem_I .shared_arith = "off";
        defparam kerr_rem_I .extended_lut = "off";
        defparam kerr_rem_I .lut_mask = 64'h2331223029110325;

        assign k_err = kerr_out_ai | kerr_out_ej | 
                    rd2_err_lc & !rd1e |
                    kerr_remainder;
    end
endgenerate

////////////////////////////////////////////////
// output registers
////////////////////////////////////////////////
always @(posedge clk or posedge rst)
begin
    if (rst)
    begin
        dout_k <= 0;
        dout_val <= 0;
        dout_dat <= 0;
        dout_rdreg <= 0;
        dout_rderr <= 0;
        dout_kerr <= 0;
    end
    else
    begin
        dout_val <= 0;
        if (din_ena)
        begin
            dout_k <= K;
            dout_val <= din_ena;
            dout_dat <= {H,G,F,E,D,C,B,A};
            dout_rdreg <= dout_rdcomb;
            dout_rderr <= (RDERR) ? (rd1_err | rd2_err) : 0;
            dout_kerr <= (KERR) ? k_err : 0;
        end
    end
end


endmodule
